#!/bin/bash
#
# Copyright (C) 2013-2020 Sysdig, Inc.
#
# This file is part of sysdig .
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

#
# This script builds a precompiled version of sysdig-probe for a bunch of kernels
# The precompiled binary is then obtained at runtime by sysdig-probe-loader
# Ideally, the community should expand this stuff with better support
#
set -euo pipefail

ARTIFACTORY_SERVER=
ARTIFACTORY_KEY=
BUILDER_IMAGE_PREFIX=
DOWNLOAD_CONCURRENCY=$(nproc)
KERNEL_TYPE=
PROBE_NAME=sysdig-probe
RETRIES=10
DOWNLOAD_TIMEOUT=300
PROBE_VERSION=
RUNNING_IN_DOCKER=
FAILED=
FAIL_LOG=$(mktemp /tmp/fail.log.XXXXXX)

usage() {
	cat >&2 <<EOF
Usage: $0 -v PROBE_VERSION [options...] [archives]

Options:
	-a ARTIFACTORY_SERVER
		Server name for non-public package downloads (RHEL kernel builds).

	-A ARTIFACTORY_KEY
		API key for Artifactory.

	-b BUILDER_IMAGE_PREFIX
		By default this script builds the required container images on the fly.
		Specify this option to skip this step and pull the images
		from a registry instead.

		The images are called <PREFIX>sysdig-probe-builder:<TAG>, where:
		- <PREFIX> is the value of this option,
		- <TAG> is chosen for every build. See the Dockerfiles shipped
		  with this script.

	-B BASEDIR
		Pass BASEDIR as the base directory to the builder containers
		This is probably only useful when running this script in a container

	-d DOWNLOAD_CONCURRENCY
		The number of parallel kernel package downloads. Set to 0 to disable
		downloads completely. Defaults to the number of CPU cores.

		Note: CoreOS images are downloaded serially, regardless of the value
		of this setting (0 is still honored).

	-k KERNEL_TYPE
		Build only for specified kernel types. The supported types are:
			AmazonLinux
			AmazonLinux2
			CentOS
			CoreOS
			Debian
			Fedora
			Fedora-Atomic
			OracleOL6
			OracleOL7
			OracleRHCK
			RHEL [requires Artifactory authentication]
			Ubuntu

		If not specified, builds all the above types (RHEL kernels will be
		skipped if no Artifactory credentials are provided).

		Additionally, you can skip the downloading of distribution kernels
		and provide your own DEBs/RPMs for the Custom* kernel types:

		$0 (...) -k CustomCentOS path/*.rpm
		$0 (...) -k CustomCoreOS path/*.bin.bz2
		$0 (...) -k CustomDebian path/*.deb
		$0 (...) -k CustomUbuntu path/*.deb

		The CustomCentOS type should also work for other RPM-based distributions,
		like Fedora, RHEL or AmazonLinux

		For CoreOS, the downloaded images must be named
		coreos_developer_container.VERSION.bin.bz2, where VERSION is
		the CoreOS release (e.g. 2345.0.0).

	-p PROBE_NAME
		The resulting module name (e.g. sysdig-probe for OSS sysdig).

	-r RETRIES
		Number of retries for kernel package downloads.

	-s SYSDIG_DIR
		The directory containing an existing Sysdig checkout in the required
		version. Specify this option to prevent this script from cloning
		the Sysdig repository.

		Note: you still need to pass -v with the right version, otherwise
		the version number and the actual code will diverge, leading to
		unpredictable behavior, including kernel crashes.

	-t DOWNLOAD_TIMEOUT
		Timeout for kernel package downloads, in seconds.

	-v VERSION
		The resulting module version. If not disabled (see -s), this script
		will clone the Sysdig repository again and check out the right
		version.

Examples:
	- build the probe for all distro kernels we know of:
	$0 -v 0.26.5

	- build the probe for all CentOS kernels
	$0 -v 0.26.5 -k CentOS

	- build the probe for a custom set of Ubuntu kernels
	$0 -v 0.26.5 -k CustomUbuntu pool/l/linux-{image,modules,headers}*.deb

	- build the probe for a specific CoreOS release
	wget http://alpha.release.core-os.net/amd64-usr/2345.0.0/coreos_developer_container.bin.bz2 -O coreos_developer_container.2345.0.0.bin.bz2
	$0 -v 0.26.5 -k CustomCoreOS coreos_developer_container.2345.0.0.bin.bz2
EOF
	exit 1
}

BASEDIR=$(pwd)
DOCKER_BASEDIR=$BASEDIR
while getopts ":a:A:b:B:d:k:p:r:s:t:v:" opt
do
	case "$opt" in
		a) ARTIFACTORY_SERVER=$OPTARG;;
		A) ARTIFACTORY_KEY=$OPTARG;;
		b) BUILDER_IMAGE_PREFIX=$OPTARG;;
		B) DOCKER_BASEDIR=$OPTARG
		   RUNNING_IN_DOCKER=1;;
		d) DOWNLOAD_CONCURRENCY=$OPTARG;;
		k) KERNEL_TYPE=$OPTARG;;
		p) PROBE_NAME=$OPTARG;;
		r) RETRIES=$OPTARG;;
		s) SYSDIG_DIR=$OPTARG;;
		t) DOWNLOAD_TIMEOUT=$OPTARG;;
		v) PROBE_VERSION=$OPTARG;;
		\?)
			echo "Invalid option $OPTARG" >&2
			usage
			;;
		:)
			echo "Option $OPTARG requires an argument" >&2
			usage
			;;
	esac
done

shift $((OPTIND - 1))

if [ -z "$PROBE_VERSION" ]
then
	echo "-v VERSION is mandatory" >&2
	usage
fi

if [ -n "${SYSDIG_DIR:-}" ]
then
	# assume sysdig in the right version is already checked out in the directory
	HAVE_SYSDIG_CHECKOUT=1
else
	SYSDIG_DIR=sysdig
	HAVE_SYSDIG_CHECKOUT=
fi

if [[ "$SYSDIG_DIR" != "/"* ]]
then
	SYSDIG_DIR=$(readlink -f $SYSDIG_DIR)
fi
ARCH=$(uname -m)
BUILDER_SOURCE=$(dirname $(readlink -f $0))

if [ ! -d $BASEDIR/output ]; then
	mkdir $BASEDIR/output
fi

PROBE_DEVICE_NAME=$(echo $PROBE_NAME | cut -f1 -d-)

if [ $PROBE_NAME = "sysdigcloud-probe" ]; then
	SYSDIG_TAG="agent/${PROBE_VERSION}"
elif [ $PROBE_NAME = "sysdig-probe" ]; then
	SYSDIG_TAG="${PROBE_VERSION}"
else
	SYSDIG_TAG="${PROBE_DEVICE_NAME}/${PROBE_VERSION}"
fi

# The previously released versions of sysdig, falco, and the agent
# don't have one of the necessary changes that this script requires
# (namely, the ability to run cmake from the sysdig directory but
# specify PROBE_NAME, PROBE_VERSION, and PROBE_DEVICE_NAME). Rather
# than change what was in the tag we made modified tags x.y.z-cfgprobe
# that include the sysdig version x.y.z but also one additional commit
# that allows making the probe name/version/etc configurable.

if [ $SYSDIG_TAG = "0.24.1" ] || [ $SYSDIG_TAG = "agent/0.85.1" ] || [ $SYSDIG_TAG = "falco/0.12.1" ]; then
	SYSDIG_TAG="${SYSDIG_TAG}-cfgprobe"
fi

function checkout_sysdig {
	if [ -z "$HAVE_SYSDIG_CHECKOUT" ]
	then
		rm -rf $SYSDIG_DIR
		mkdir -p $SYSDIG_DIR
		git clone git@github.com:draios/sysdig.git $SYSDIG_DIR
		(cd $SYSDIG_DIR && git checkout $SYSDIG_TAG)
	fi
	HAVE_SYSDIG_CHECKOUT=1
}

function build_toolkit {
	if [ -z "${HAVE_BUILDER_TOOLKIT:-}" ]
	then
		IMAGE_NAME="${BUILDER_IMAGE_PREFIX:-}sysdig-probe-builder:toolkit"
		docker build -t "$IMAGE_NAME" -f $BUILDER_SOURCE/Dockerfile.toolkit --pull $BUILDER_SOURCE
		HAVE_BUILDER_TOOLKIT=1
	fi
}

function run_toolkit {
	if [ -z "$RUNNING_IN_DOCKER" ]
	then
		build_toolkit
	fi
	declare -a DOCKER_OPTS
	declare -a TOOLKIT_OPTS

	IN_TOOLKIT_OPTS=
	for i in "$@"
	do
		if [ -z "$IN_TOOLKIT_OPTS" ]
		then
			if [ "$i" == "--" ]
			then
				IN_TOOLKIT_OPTS=1
			else
				DOCKER_OPTS+=("$i")
			fi
		else
			TOOLKIT_OPTS+=("$i")
		fi
	done

	if [ -z "$RUNNING_IN_DOCKER" ]
	then
		# running on the host, use toolkit container
		docker run --rm --privileged ${DOCKER_OPTS[@]} "${BUILDER_IMAGE_PREFIX:-}sysdig-probe-builder:toolkit" ${TOOLKIT_OPTS[@]}
	else
		# already in a container, call the toolkit directly
		/builder/toolkit-entrypoint.sh ${TOOLKIT_OPTS[@]}
	fi
}

declare -A builders
function build_probe_impl {
	PROBE_ROOT=${PROBE_ROOT:-/build/probe}
	if [ -n "${DOCKER_BUILDER:-}" ]
	then
		DOCKERFILE=$BUILDER_SOURCE/Dockerfile.$DOCKER_BUILDER
		DOCKERFILE_TAG=$DOCKER_BUILDER

		echo "Forced use of builder $DOCKERFILE_TAG"
	elif [ -n "${BUILDER_DISTRO:-}" ]
	then
		# Try to find the gcc version used to build this particular kernel
		# Check CONFIG_GCC_VERSION=90201 in the kernel config first
		# as 5.8.0 seems to have a different format for the LINUX_COMPILER string
		if [ -e $KERNELDIR/include/generated/autoconf.h ]
		then
			BUILDER_GCC_VERSION=$(grep -Po '(?<=^#define CONFIG_GCC_VERSION ).*' $KERNELDIR/include/generated/autoconf.h | sed '-res@(.*)(..)(..)$@\1\.\2\.\3@' '-es@\.0@\.@g')
		else
			BUILDER_GCC_VERSION=
		fi

		if [ -z "$BUILDER_GCC_VERSION" ]
		then
			if [ -e $KERNELDIR/include/generated/compile.h ]
			then
				BUILDER_GCC_VERSION=$(grep -Po '(?<=^#define LINUX_COMPILER "gcc version )[0-9.]+' $KERNELDIR/include/generated/compile.h || true)
			elif [ -e $KERNELDIR/include/linux/compile.h ]
			then
				# RHEL6
				BUILDER_GCC_VERSION=$(grep -Po '(?<=^#define LINUX_COMPILER "gcc version )[0-9.]+' $KERNELDIR/include/linux/compile.h || true)
			else
				# ancient Ubuntu gets and ancient compiler
				BUILDER_GCC_VERSION=4.8.0
			fi
		fi

		if [ -z "$BUILDER_GCC_VERSION" ]
		then
			echo "Failed to find gcc version for $KERNELDIR"
			return 1
		fi

		# We don't really care about the compiler patch levels, only the major/minor version
		BUILDER_GCC_MINOR_VERSION="${BUILDER_GCC_VERSION%.*}"

		# Choose the right gcc version from the ones we have available (as Docker images)
		# - if we have the exact minor version, use it
		# - if not, and there's a newer compiler version, use that
		#   (as close to the requested version as possible)
		# - if there are no newer compilers, use the newest one we have
		#   it will be older than the requested one but hopefully
		#   not by much
		#
		# This means we don't have to exactly follow all distro gcc versions
		# (indeed, we don't e.g. for AmazonLinux) but only need to add a new
		# Dockerfile when the latest kernel fails to build with our newest
		# gcc for that distro

		# The dockerfiles in question all look like .../Dockerfile.centos-gcc4.4
		# or similar. We want to pick the one that's closest to $BUILDER_GCC_MINOR_VERSION
		# (exact match, slightly newer, slightly older, in that order of preference).
		# To do that, we iterate over the list of all available dockerfiles (i.e. gcc
		# versions) for a particular distribution in ascending version order (oldest->newest).
		# Sadly, sort -V falls back to ASCII order with non-numeric prefixes, i.e.
		# these are sorted according to sort -V:
		#   Dockerfile.centos-gcc10.0
		#   Dockerfile.centos-gcc4.4
		#   Dockerfile.centos-gcc9.2
		# So, to get actual sorting by version numbers, we strip the common prefix first
		# and add it back after finding the best available version. What we're sorting is:
		#   4.4
		#   9.2
		#   10.0
		# and now we properly realize that gcc 10 is newer than 9.2, not older than 4.4
		DOCKERFILE_PREFIX=$BUILDER_SOURCE/Dockerfile.${BUILDER_DISTRO}-gcc
		CHOSEN_GCC_VERSION=$(ls $DOCKERFILE_PREFIX* | sed "s@$DOCKERFILE_PREFIX@@" | sort -V | awk \
			-v target=${BUILDER_GCC_MINOR_VERSION} \
		'
		$1 < target { older = $1 }
		$1 == target { exact = $1 }
		$1 > target && !newer { newer = $1 }

		END {
			if (exact) { print exact }
			else if (newer) { print newer }
			else { print older }
		}
		')
		DOCKERFILE=${DOCKERFILE_PREFIX}${CHOSEN_GCC_VERSION}
		DOCKERFILE_TAG=${DOCKERFILE#$BUILDER_SOURCE/Dockerfile.}
		echo "Selected builder $DOCKERFILE_TAG for $BUILDER_DISTRO, gcc $BUILDER_GCC_VERSION"
	fi

	if [ ! -e $DOCKERFILE ]
	then
		echo "Cannot find $DOCKERFILE"
		return 1
	fi

	IMAGE_NAME="sysdig-probe-builder:${DOCKERFILE_TAG}"
	CONTAINER_NAME="sysdig-probe-builder-${DOCKERFILE_TAG}"

	docker ps -q -f "name=$CONTAINER_NAME" | xargs --no-run-if-empty docker rm || return 1
	if [ -n "$BUILDER_IMAGE_PREFIX" ]
	then
		IMAGE_NAME="${BUILDER_IMAGE_PREFIX}${IMAGE_NAME}"
	else
		if [ -z "${builders[$IMAGE_NAME]:-}" ]
		then
			docker build -t "$IMAGE_NAME" -f $DOCKERFILE --pull $BUILDER_SOURCE || return 1
			docker images -q -f 'dangling=true' | xargs --no-run-if-empty docker rmi || true
			builders[$IMAGE_NAME]=1
		fi
	fi

	docker run --rm -v $DOCKER_BASEDIR:$PROBE_ROOT -v $SYSDIG_DIR:$PROBE_ROOT/sysdig \
		-e OUTPUT=$PROBE_ROOT/output \
		-e PROBE_NAME=$PROBE_NAME \
		-e PROBE_VERSION=$PROBE_VERSION \
		-e PROBE_DEVICE_NAME=$PROBE_DEVICE_NAME \
		-e KERNELDIR=$PROBE_ROOT/${KERNELDIR#$BASEDIR} \
		-e KERNEL_RELEASE=$KERNEL_RELEASE \
		-e HASH=$HASH \
		-e HASH_ORIG=$HASH_ORIG \
		--name $CONTAINER_NAME \
		$IMAGE_NAME || return 1
}

function build_probe {
	PROBE_ID=$PROBE_NAME-$PROBE_VERSION-$ARCH-$KERNEL_RELEASE-$HASH.ko

	# Skip Kernel 4.15.0-29 because probe does not build against it
	if [ $KERNEL_RELEASE-$HASH = "4.15.0-29-generic-ea0aa038a6b9bdc4bb42152682bba6ce"  ]; then
		echo "Temporarily skipping $PROBE_ID"
		return
	fi

	if [ -f $BASEDIR/output/$PROBE_NAME-$PROBE_VERSION-$ARCH-$KERNEL_RELEASE-$HASH.ko ] &&
	   [ -f $BASEDIR/output/$PROBE_NAME-$PROBE_VERSION-$ARCH-$KERNEL_RELEASE-$HASH_ORIG.ko ]; then
		echo "Skipping $PROBE_ID (already built)"
		return
	fi

    LOG=$(mktemp /tmp/log.XXXXXX)
    if ! build_probe_impl &> $LOG
    then
        (echo "Build for $PROBE_ID failed"; cat $LOG) | tee -a $FAIL_LOG
        FAILED=1
    else
		echo "Build for $PROBE_ID successful"
		cat $LOG
    fi
    rm -f $LOG
}

function coreos_build {
	BUILDER_DISTRO=${BUILDER_DISTRO:-centos}
	for IMG in "$@"
	do
		VERSION=${IMG##*/coreos_developer_container.}
		VERSION=${VERSION%.bin.bz2}
		VERSION_DIR=build/$DISTRO/$VERSION
		if [ ! -e $VERSION_DIR/config_orig ]
		then
			mkdir -p $VERSION_DIR
			echo "Unpacking $(basename $IMG)"
			FULL_IMG=$(readlink -f "$IMG")
			FULL_VERSION_DIR=$(readlink -f "$VERSION_DIR")
			run_toolkit -v "$FULL_IMG:$FULL_IMG:ro" -v "$FULL_VERSION_DIR:$FULL_VERSION_DIR" -- coreos "$FULL_IMG" "$FULL_VERSION_DIR"

			KERNEL_RELEASE=$(cd $VERSION_DIR && ls config-* | sed s/config-//)
			export COREOS_BUILD=${VERSION%%.*}
			if [ $COREOS_BUILD -gt 890 ]
			then
				# https://groups.google.com/forum/#!topic/coreos-dev/Z8Q7sIy6YwE
				sed -i 's/CONFIG_INITRAMFS_SOURCE=""/CONFIG_INITRAMFS_SOURCE="bootengine.cpio"\nCONFIG_INITRAMFS_ROOT_UID=0\nCONFIG_INITRAMFS_ROOT_GID=0/' $VERSION_DIR/config-*
				sed -i 'N;s/\(CONFIG_RD_LZ4=.\)\n\(CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE=\)/\1\nCONFIG_INITRAMFS_COMPRESSION=".gz"\n\2/' $VERSION_DIR/config-*
			else
				mkdir -p build/$DISTRO/vanilla
				VANILLA=$(echo $KERNEL_RELEASE | sed s/[-+].*// | sed s/\.0$//)
				MAJOR=$(echo $KERNEL_RELEASE | head -c1)
				EXTRAVERSION=$(echo $KERNEL_RELEASE | sed s/[^-+]*//)
				TGZ_NAME=linux-${VANILLA}.tar.xz
				DIR_NAME=linux-${VANILLA}
				KERNEL_URL=https://www.kernel.org/pub/linux/kernel/v${MAJOR}.x/$TGZ_NAME
				wget -c -nv --timeout=${DOWNLOAD_TIMEOUT} --tries=${RETRIES} $KERNEL_URL -O build/$DISTRO/vanilla/$TGZ_NAME
				if [ ! -e build/$DISTRO/vanilla/$DIR_NAME ]
				then
					(cd build/$DISTRO/vanilla && tar xf $TGZ_NAME)
				fi
			fi
		fi

		KERNEL_RELEASE=$(cd $VERSION_DIR && ls config-* | sed s/config-//)
		COREOS_BUILD=${VERSION%%.*}
		HASH_ORIG=$(md5sum $VERSION_DIR/config_orig | cut -d' ' -f1)
		HASH=$(md5sum $VERSION_DIR/config-* | cut -d' ' -f1)
		echo "Building probe for CoreOS kernel $KERNEL_RELEASE (CoreOS build $COREOS_BUILD)"
		if [ -f $BASEDIR/output/$PROBE_NAME-$PROBE_VERSION-$ARCH-$KERNEL_RELEASE-$HASH.ko ] &&
		   [ -f $BASEDIR/output/$PROBE_NAME-$PROBE_VERSION-$ARCH-$KERNEL_RELEASE-$HASH_ORIG.ko ]; then
			echo "Skipping $PROBE_NAME-$PROBE_VERSION-$ARCH-$KERNEL_RELEASE-$HASH.ko (already built)"
			continue
		fi

		if [ $COREOS_BUILD -gt 890 ]
		then
			unset DOCKER_BUILDER
			export KERNELDIR=$BASEDIR/$VERSION_DIR/modules/$KERNEL_RELEASE/build
		else
			VANILLA=$(echo $KERNEL_RELEASE | sed s/[-+].*// | sed s/\.0$//)
			EXTRAVERSION=$(echo $KERNEL_RELEASE | sed s/[^-+]*//)
			export KERNELDIR=$BASEDIR/build/$DISTRO/vanilla/linux-$VANILLA
			make distclean -C $KERNELDIR
			sed -i "s/^EXTRAVERSION.*/EXTRAVERSION = $EXTRAVERSION/" $KERNELDIR/Makefile
			cp $VERSION_DIR/config_orig $KERNELDIR/.config
			DOCKER_BUILDER=coreos-old
		fi
		build_probe

	done
}

function ubuntu_build {
	BUILDER_DISTRO=${BUILDER_DISTRO:-ubuntu}
	for DEB in "$@"
	do
		KERNEL_RELEASE_FULL=$(echo $DEB | grep -E -o "[0-9]{1}\.[0-9]+\.[0-9]+-[0-9]+\.[0-9][^_]*")	# ex. 3.13.0-24.47
		KERNEL_RELEASE=$(echo "$KERNEL_RELEASE_FULL" | grep -Eo '[^-]+-[0-9]+')                     # ex. 3.13.0-24
		KERNEL_UPDATE=${KERNEL_RELEASE_FULL#$KERNEL_RELEASE.}                                       # ex. 47

		TARGET=build/$BUILDER_DISTRO/$KERNEL_RELEASE/$KERNEL_UPDATE
		MARKER=$TARGET/.$(basename $DEB)
		if [ -e $MARKER ]
		then
			echo "$DEB already unpacked in $TARGET"
		else
			echo "Unpacking $DEB to $TARGET"
			mkdir -p $TARGET
			dpkg -x $DEB $TARGET
			touch $MARKER
		fi
	done

	for DEB in "$@"
	do
		if [[ $(basename $DEB) != "linux-image-"*".deb" ]]
		then
			continue
		fi
		KERNEL_RELEASE_FULL=$(echo $DEB | grep -Eo "[0-9]{1}\.[0-9]+\.[0-9]+-[0-9]+\.[0-9][^_]*")	# ex. 3.13.0-24.47
		KERNEL_RELEASE=$(echo "$KERNEL_RELEASE_FULL" | grep -Eo '[^-]+-[0-9]+')                     # ex. 3.13.0-24
		KERNEL_UPDATE=${KERNEL_RELEASE_FULL#$KERNEL_RELEASE.}                                       # ex. 47

		KERNEL_VERSION=$(echo $DEB | grep -Eo "[0-9]{1}\.[0-9]+\.[0-9]+-[0-9]+-[a-z]+")             # ex. 3.13.0-24-generic

		TARGET=build/$BUILDER_DISTRO/$KERNEL_RELEASE/$KERNEL_UPDATE
		HASH=$(md5sum $TARGET/boot/config-$KERNEL_VERSION | cut -d' ' -f1)
		HASH_ORIG=$HASH

		export KERNELDIR=$BASEDIR/$TARGET/usr/src/linux-headers-$KERNEL_VERSION

		# impedance mismatch
		KERNEL_RELEASE=$KERNEL_VERSION
		echo "Building probe for Ubuntu kernel $KERNEL_VERSION ($KERNEL_RELEASE_FULL)"
		build_probe
	done
}

function rhel_build {
	BUILDER_DISTRO=${BUILDER_DISTRO:-centos}
	for RPM in "$@"
	do
		RPM_FILE=$(basename $RPM)
		KERNEL_RELEASE=$(echo $RPM_FILE | gawk 'match($0, /[^kernel\-(uek\-)?(core\-|devel\-)?].*[^(\.rpm)]/){ print substr($0, RSTART, RLENGTH) }')

		TARGET=build/$DISTRO/$KERNEL_RELEASE
		MARKER=$TARGET/.$RPM_FILE
		if [ -e $MARKER ]
		then
			echo "$RPM already unpacked in $TARGET"
		else
			echo "Unpacking $RPM to $TARGET"
			mkdir -p $TARGET
			FULL_RPM=$(readlink -f "$RPM")
			FULL_TARGET=$(readlink -f "$TARGET")
			run_toolkit -v "$FULL_RPM:$FULL_RPM:ro" -v "$FULL_TARGET:$FULL_TARGET" -- rpm "$FULL_RPM" "$FULL_TARGET"
			touch $MARKER
		fi
	done

	for RPM in "$@"
	do
		RPM_FILE=$(basename $RPM)
		KERNEL_RELEASE=$(echo $RPM_FILE | gawk 'match($0, /[^kernel\-(uek\-)?(core\-|devel\-)?].*[^(\.rpm)]/){ print substr($0, RSTART, RLENGTH) }')
		TARGET=build/$DISTRO/$KERNEL_RELEASE

		if [ -f $TARGET/boot/config-$KERNEL_RELEASE ]; then
			HASH=$(md5sum $TARGET/boot/config-$KERNEL_RELEASE | cut -d' ' -f1)
		else
			HASH=$(md5sum $TARGET/lib/modules/$KERNEL_RELEASE/config | cut -d' ' -f1)
		fi
		HASH_ORIG=$HASH
		export KERNELDIR=$BASEDIR/$TARGET/usr/src/kernels/$KERNEL_RELEASE
		echo "Building probe for CentOS kernel $KERNEL_RELEASE"
		build_probe
	done
}

function debian_build {
	BUILDER_DISTRO=${BUILDER_DISTRO:-debian}
	for DEB in "$@"
	do
		DEB_FILE=$(basename $DEB)
		if [[ "$DEB_FILE" = *"kbuild"* ]]
		then
			continue
		fi
		KERNEL_RELEASE=$(echo ${DEB_FILE} | grep -E -o "[0-9]{1}\.[0-9]+\.[0-9]+(-[0-9]+)?"| head -1)
		KERNEL_MAJOR=$(echo ${KERNEL_RELEASE} | grep -E -o "[0-9]{1}\.[0-9]+")
		PACKAGE=$(echo ${DEB_FILE} | grep -E -o "(common_[0-9]{1}\.[0-9]+.*(amd64|all)|amd64_[0-9]{1}\.[0-9]+.*amd64)" | sed -E 's/(common_|amd64_|_amd64|all_|_all)//g')

		TARGET=build/$BUILDER_DISTRO/$KERNEL_RELEASE
		MARKER=$TARGET/.$(basename $DEB)
		if [ -e $MARKER ]
		then
			echo "$DEB already unpacked in $TARGET"
		else
			echo "Unpacking $DEB to $TARGET"
			mkdir -p $TARGET
			dpkg -x $DEB $TARGET
			touch $MARKER
		fi
		set +e
		KBUILD_PACKAGE=$(ls $DISTRO/linux-kbuild-${KERNEL_MAJOR}_*.deb 2>/dev/null | tail -1)
		set -e
		if [ -n "$KBUILD_PACKAGE" ]
		then
			MARKER=$TARGET/.$(basename $KBUILD_PACKAGE)
			if [ -e $MARKER ]
			then
				echo "$KBUILD_PACKAGE already unpacked in $TARGET"
			else
				echo "Unpacking $KBUILD_PACKAGE to $TARGET"
				mkdir -p $TARGET
				dpkg -x $KBUILD_PACKAGE $TARGET
				touch $MARKER
			fi
		fi
	done

	for DEB in "$@"
	do
		DEB_FILE=$(basename $DEB)
		if [[ "$DEB_FILE" != "linux-image-"*".deb" ]]
		then
			continue
		fi
		KERNEL_RELEASE=$(echo ${DEB_FILE} | grep -E -o "[0-9]{1}\.[0-9]+\.[0-9]+(-[0-9]+)?"| head -1)
		KERNEL_ARCH_RELEASE=$(echo ${DEB_FILE} | grep -E -o "[0-9]{1}\.[0-9]+\.[0-9]+(-[0-9]+)?-amd64"| head -1)
		KERNEL_COMMON_RELEASE=${KERNEL_ARCH_RELEASE/amd64/common}

		TARGET=build/$BUILDER_DISTRO/$KERNEL_RELEASE
		HASH=$(md5sum $TARGET/boot/config-$KERNEL_ARCH_RELEASE | cut -d' ' -f1)
		HASH_ORIG=$HASH

		export KERNELDIR=$BASEDIR/$TARGET/usr/src/linux-headers-$KERNEL_ARCH_RELEASE
		export KERNELDIR_COMMON=$BASEDIR/$TARGET/usr/src/linux-headers-$KERNEL_COMMON_RELEASE

		CONTAINER_KERNELDIR=/build/probe/$TARGET/usr/src/linux-headers-$KERNEL_ARCH_RELEASE
		CONTAINER_KERNELDIR_COMMON=/build/probe/$TARGET/usr/src/linux-headers-$KERNEL_COMMON_RELEASE

		BUILD_LINK=$(readlink $BASEDIR/$TARGET/lib/modules/$KERNEL_ARCH_RELEASE/build)
		case $BUILD_LINK in
			/*)
				BUILD_LINK=../../../${BUILD_LINK#/}
				ls -l $BASEDIR/$TARGET/lib/modules/$KERNEL_ARCH_RELEASE/build
				ln -sf $BUILD_LINK $BASEDIR/$TARGET/lib/modules/$KERNEL_ARCH_RELEASE/build
				;;
		esac
		ls -l $BASEDIR/$TARGET/lib/modules/$KERNEL_ARCH_RELEASE/build

		SOURCE_LINK=$(readlink $BASEDIR/$TARGET/lib/modules/$KERNEL_ARCH_RELEASE/source)
		case $SOURCE_LINK in
			/*)
				SOURCE_LINK=../../../${SOURCE_LINK#/}
				ls -l $BASEDIR/$TARGET/lib/modules/$KERNEL_ARCH_RELEASE/source
				ln -sf $SOURCE_LINK $BASEDIR/$TARGET/lib/modules/$KERNEL_ARCH_RELEASE/source
				;;
		esac
		ls -l $BASEDIR/$TARGET/lib/modules/$KERNEL_ARCH_RELEASE/source

		MAKEFILE=$BASEDIR/$TARGET/usr/src/linux-headers-$KERNEL_ARCH_RELEASE/Makefile

		if [ ! -e $MAKEFILE.sysdig-orig ]
		then
			cp $MAKEFILE $MAKEFILE.sysdig-orig
			cat $MAKEFILE.sysdig-orig | \
			sed '0,/MAKEARGS.*$/s||MAKEARGS := -C '"${CONTAINER_KERNELDIR_COMMON}"' O='"${CONTAINER_KERNELDIR}"'|' | \
			sed 's/@://' | \
			sed 's|$(cmd) %.*$|$(cmd) : all|' > $MAKEFILE
		fi

		# impedance mismatch
		KERNEL_RELEASE=$KERNEL_ARCH_RELEASE
		echo "Building probe for Debian kernel $KERNEL_ARCH_RELEASE"
		build_probe
	done
}

function build_group_ubuntu {
	#
	# Ubuntu build
	#

	DISTRO=ubuntu
	BUILDER_DISTRO=ubuntu
	if [ "$DOWNLOAD_CONCURRENCY" != "0" ]
	then
		echo Downloading Ubuntu kernels
		mkdir -p $DISTRO
		$BUILDER_SOURCE/kernel-crawler.py Ubuntu | xargs -n 1 -P $DOWNLOAD_CONCURRENCY wget -c -nv --timeout=${DOWNLOAD_TIMEOUT} --tries=${RETRIES} -P $DISTRO
	fi

	echo "Building Ubuntu"
	ubuntu_build $DISTRO/*.deb
}

function build_group_centos {
	#
	# RHEL build through CentOS
	#

	DISTRO=centos
	BUILDER_DISTRO=centos
	if [ "$DOWNLOAD_CONCURRENCY" != "0" ]
	then
		echo Downloading CentOS kernels
		DIR=$(dirname $(readlink -f $0))
		mkdir -p $DISTRO
		$DIR/kernel-crawler.py CentOS | xargs -n 1 -P $DOWNLOAD_CONCURRENCY wget -c -nv --timeout=${DOWNLOAD_TIMEOUT} --tries=${RETRIES} -P $DISTRO
	fi

	echo Building CentOS
	rhel_build $DISTRO/*.rpm
}

function build_group_fedora {
	#
	# Fedora build
	#

	DISTRO=fedora
	BUILDER_DISTRO=centos
	if [ "$DOWNLOAD_CONCURRENCY" != "0" ]
	then
		echo Downloading Fedora kernels
		DIR=$(dirname $(readlink -f $0))
		mkdir -p $DISTRO
		$DIR/kernel-crawler.py Fedora | xargs -n 1 -P $DOWNLOAD_CONCURRENCY wget -c -nv --timeout=${DOWNLOAD_TIMEOUT} --tries=${RETRIES} -P $DISTRO
	fi

	echo Building Fedora
	rhel_build $DISTRO/*.rpm
}

function build_group_fedora_atomic {
	#
	# Fedora Atomic build
	#

	DISTRO=fedora-atomic
	BUILDER_DISTRO=centos
	if [ "$DOWNLOAD_CONCURRENCY" != "0" ]
	then
		echo Downloading Fedora Atomic kernels
		DIR=$(dirname $(readlink -f $0))
		mkdir -p $DISTRO
		$DIR/kernel-crawler.py Fedora-Atomic | xargs -n 1 -P $DOWNLOAD_CONCURRENCY wget -c -nv --timeout=${DOWNLOAD_TIMEOUT} --tries=${RETRIES} -P $DISTRO
	fi

	echo Building Fedora Atomic
	rhel_build $DISTRO/*.rpm
}

function build_group_coreos {
	#
	# CoreOS build
	#

	DISTRO=coreos
	BUILDER_DISTRO=centos

	if [ "$DOWNLOAD_CONCURRENCY" != "0" ]
	then
		echo Downloading CoreOS kernels
		DIR=$(dirname $(readlink -f $0))
		mkdir -p $DISTRO
		$DIR/kernel-crawler.py CoreOS | while read URL
		do
			COREOS_VERSION=$(curl -fsSL ${URL}version.txt | grep -Po '(?<=^COREOS_VERSION=).*')
			wget -O $DISTRO/coreos_developer_container.$COREOS_VERSION.bin.bz2 -c -nv --timeout=${DOWNLOAD_TIMEOUT} --tries=${RETRIES} ${URL}coreos_developer_container.bin.bz2
		done
	fi

	echo Building CoreOS
	coreos_build $DISTRO/*.bin.bz2
}

function build_group_debian {
	#
	# Debian build
	#
	DISTRO=debian
	BUILDER_DISTRO=debian
	if [ "$DOWNLOAD_CONCURRENCY" != "0" ]
	then
		echo Downloading Debian kernels
		DIR=$(dirname $(readlink -f $0))
		mkdir -p $DISTRO
		$DIR/kernel-crawler.py Debian | xargs -n 1 -P $DOWNLOAD_CONCURRENCY wget -c -nv --timeout=${DOWNLOAD_TIMEOUT} --tries=${RETRIES} -P $DISTRO
	fi

	echo "Building Debian"
	debian_build $DISTRO/*.deb
}

function build_group_oracle_rhck {
	#
	# Oracle RHCK build
	#
	DISTRO=oracle-rhck
	BUILDER_DISTRO=centos
	if [ "$DOWNLOAD_CONCURRENCY" != "0" ]
	then
		echo Downloading Oracle RHCK kernels
		DIR=$(dirname $(readlink -f $0))
		mkdir -p $DISTRO
		$DIR/oracle-kernel-crawler.py Oracle-RHCK | xargs -n 1 wget -c -nv --timeout=${DOWNLOAD_TIMEOUT} --tries=${RETRIES} -P $DISTRO
	fi
	echo "Building Oracle RHCK"
	rhel_build $DISTRO/*.rpm
}

function build_group_oracle_ol6_uek {
	#
	# Oracle Linux 6 UEK build
	#
	DISTRO=oracle-uek6
	BUILDER_DISTRO=oracle-uek6
	DOCKER_BUILDER=ol6
	if [ "$DOWNLOAD_CONCURRENCY" != "0" ]
	then
		echo Downloading Oracle Linux 6 UEK kernels
		DIR=$(dirname $(readlink -f $0))
		mkdir -p $DISTRO
		$DIR/oracle-kernel-crawler.py OL6-UEK | xargs -n 1 wget -c -nv --timeout=${DOWNLOAD_TIMEOUT} --tries=${RETRIES} -P $DISTRO
	fi
	echo "Building Oracle Linux 6 UEK"
	rhel_build $DISTRO/*.rpm
	unset DOCKER_BUILDER
}

function build_group_oracle_ol7_uek {
	#
	# Oracle Linux 7 UEK build
	#
	DISTRO=oracle-uek7
	BUILDER_DISTRO=oracle-uek7
	DOCKER_BUILDER=ol7
	if [ "$DOWNLOAD_CONCURRENCY" != "0" ]
	then
		echo Downloading Oracle Linux 7 UEK kernels
		DIR=$(dirname $(readlink -f $0))
		mkdir -p $DISTRO
		$DIR/oracle-kernel-crawler.py OL7-UEK | xargs -n 1 wget -c -nv --timeout=${DOWNLOAD_TIMEOUT} --tries=${RETRIES} -P $DISTRO
	fi
	echo "Building Oracle Linux 7 UEK"
	rhel_build $DISTRO/*.rpm
	unset DOCKER_BUILDER
}

function build_group_amazonlinux {
	#
	# AmazonLinux build
	#
	DISTRO=amazonlinux
	BUILDER_DISTRO=centos
	if [ "$DOWNLOAD_CONCURRENCY" != "0" ]
	then
		echo Downloading AmazonLinux kernels
		DIR=$(dirname $(readlink -f $0))
		mkdir -p $DISTRO
		$DIR/kernel-crawler.py AmazonLinux | xargs -n 1 -P $DOWNLOAD_CONCURRENCY wget -c -nv --timeout=${DOWNLOAD_TIMEOUT} --tries=${RETRIES} -P $DISTRO
	fi
	echo "Building AmazonLinux"
	rhel_build $DISTRO/*.rpm
}

function build_group_amazonlinux2 {
	#
	# AmazonLinux build
	#
	DISTRO=amazonlinux2
	BUILDER_DISTRO=centos
	if [ "$DOWNLOAD_CONCURRENCY" != "0" ]
	then
		echo Downloading AmazonLinux2 kernels
		DIR=$(dirname $(readlink -f $0))
		mkdir -p $DISTRO
		$DIR/kernel-crawler.py AmazonLinux2 | xargs -n 1 -P $DOWNLOAD_CONCURRENCY wget -c -nv --timeout=${DOWNLOAD_TIMEOUT} --tries=${RETRIES} -P $DISTRO
	fi
	echo "Building AmazonLinux2"
	rhel_build $DISTRO/*.rpm
}

function build_group_rhel {
	if [ -z "$ARTIFACTORY_KEY" -o -z "$ARTIFACTORY_SERVER" ]
	then
		echo "Artifactory credentials missing, skipping RHEL build"
		return
	fi

	#
	# RHEL build from Stored Sources
	#
	DISTRO=rhel
	BUILDER_DISTRO=centos
	if [ "$DOWNLOAD_CONCURRENCY" != "0" ]
	then
		echo Downloading RHEL kernels
		mkdir -p $DISTRO

		# We're using aql to query for all of the objects in the repo
		# and jq to parse the json to get the rpms
		curl -s -H 'Content-Type: text/plain' -H "X-JFrog-Art-Api: $ARTIFACTORY_KEY" -X POST \
			-d 'items.find({"repo":"redhat-sources"})' https://$ARTIFACTORY_SERVER/artifactory/api/search/aql | \
			jq -r '.results[].name as $o | $o | select(endswith(".rpm")) | $o' | \
			xargs -n 1 -P $DOWNLOAD_CONCURRENCY -iRPM wget -c -nv --timeout=${DOWNLOAD_TIMEOUT} --tries=${RETRIES} -P $DISTRO https://$ARTIFACTORY_SERVER/artifactory/redhat-sources/RPM --header "X-JFrog-Art-Api: $ARTIFACTORY_KEY"
	fi

	echo Building RHEL from Stored Sources
	rhel_build $DISTRO/*.rpm
}

function build_everything {
	build_group_rhel
	build_group_ubuntu
	build_group_centos
	build_group_fedora
	build_group_fedora_atomic
	build_group_coreos
	build_group_debian
	build_group_oracle_rhck
	build_group_oracle_ol6_uek
	build_group_oracle_ol7_uek
	build_group_amazonlinux
	build_group_amazonlinux2
}

case "$KERNEL_TYPE" in
	"")
		checkout_sysdig
		build_everything
		;;
	Ubuntu)
		checkout_sysdig
		build_group_ubuntu
		;;
	CentOS)
		checkout_sysdig
		build_group_centos
		;;
	Fedora)
		checkout_sysdig
		build_group_fedora
		;;
	Fedora-Atomic)
		checkout_sysdig
		build_group_fedora_atomic
		;;
	CoreOS)
		checkout_sysdig
		build_group_coreos
		;;
	Debian)
		checkout_sysdig
		build_group_debian
		;;
	OracleRHCK)
		checkout_sysdig
		build_group_oracle_rhck
		;;
	OracleOL6)
		checkout_sysdig
		build_group_oracle_ol6_uek
		;;
	OracleOL7)
		checkout_sysdig
		build_group_oracle_ol7_uek
		;;
	AmazonLinux)
		checkout_sysdig
		build_group_amazonlinux
		;;
	AmazonLinux2)
		checkout_sysdig
		build_group_amazonlinux2
		;;
	RHEL)
		checkout_sysdig
		build_group_rhel
		;;
	CustomCentOS)
		checkout_sysdig
		DISTRO=custom-centos
		rhel_build "$@"
		;;
	CustomCoreOS)
		checkout_sysdig
		DISTRO=custom-coreos
		coreos_build "$@"
		;;
	CustomDebian)
		checkout_sysdig
		DISTRO=custom-debian
		debian_build "$@"
		;;
	CustomUbuntu)
		checkout_sysdig
		DISTRO=custom-ubuntu
		ubuntu_build "$@"
		;;
	*)
# Note: if you add a new KERNEL_TYPE here, please remember to update
#       the usage message near the top of this file
		exit 1
		;;
esac

if [ -s "$FAIL_LOG" ]
then
	echo "Failed builds:"
	echo "------------------------------"
	cat $FAIL_LOG
fi
rm -f $FAIL_LOG
if [ -n "$FAILED" ]
then
	echo "Build failed."
	exit 1
else
	echo "Success."
fi

# vim: :set tabstop=8 shiftwidth=8 noexpandtab:
